             package com.java.diagnostics.visualizer.recommender;
             
             import com.java.diagnostics.visualizer.data.AggregateData;
             import com.java.diagnostics.visualizer.data.DataPoint;
             import com.java.diagnostics.visualizer.data.TupleData;
             import com.java.diagnostics.visualizer.parser.vgc.constants.VGCGCScopes;
             import com.java.diagnostics.visualizer.recommender.util.RecommendationLabels;
             
             public class LookForExcessiveCompaction
               extends RecommendationBase
               implements Recommendation
             {
               private static final String COMPACT_FORCED = "forced gc with compaction";
               private static final String COMPACT_ALWAYS = "forced compaction";
               private static final String COMPACT_FRAGMENTED = "heap fragmented";
               private static final String COMPACT_AVOID_DESPERATE = "low free space (less than 4%)";
               private static final String COMPACT_MEMORY_INSUFFICIENT = "very low free space (less than 128kB)";
               
               public void recommend(AggregateData data)
               {
                 TupleData amountCompacted = data
                   .getTupleData("VGCLabels.amount.compacted");
                 
                 if ((amountCompacted != null) && (!amountCompacted.isEmpty())) {
                   DataPoint[] compacts = amountCompacted.getDataPoints();
                   int forced = 0;
                   double fragmented = 0.0D;
                   int lowMem = 0;
                   
                   for (DataPoint compact : compacts) {
                     String comment = compact.getComment();
                     if (("forced gc with compaction".equalsIgnoreCase(comment)) || 
                       ("forced compaction".equalsIgnoreCase(comment))) {
                       forced++;
                     } else if ("heap fragmented".equalsIgnoreCase(comment)) {
                       fragmented += 1.0D;
                     } else if (("low free space (less than 4%)".equalsIgnoreCase(comment)) || 
                     
                       ("very low free space (less than 128kB)".equalsIgnoreCase(comment))) {
                       lowMem++;
                     }
                   }
                   if (lowMem > 0) {
                     addProblem(data, RecommendationLabels.COMPACTION_LOW_MEMORY + 
                       RecommendationLabels.INCREASE_HEAP);
                   } else if (forced > 0) {
                     addProblem(data, RecommendationLabels.COMPACTION_FORCED);
                   } else if (fragmented > 0.0D) {
                     String gcMode = (String)getGCMode(data);
                     double globals = countGlobalPauses(data, gcMode);
                     
                     double percent = fragmented / globals * 100.0D;
                     if ("gencon".equalsIgnoreCase(gcMode)) {
                       if (percent > 5.0D) {
                         addProblem(data, 
                           RecommendationLabels.COMPACTION_FRAGMENTED + 
                           RecommendationLabels.INCREASE_HEAP);
                       }
                     } else if ((("optavgpause".equalsIgnoreCase(gcMode)) || 
                       ("optthruput".equalsIgnoreCase(gcMode))) && 
                       (percent > 15.0D)) {
                       addProblem(data, 
                         RecommendationLabels.COMPACTION_FRAGMENTED + 
                         RecommendationLabels.INCREASE_HEAP);
                     }
                   }
                 }
               }
               
               private double countGlobalPauses(AggregateData data, String gcMode)
               {
                 TupleData scopeRecord = data.getTupleData("VGCLabels.gc.scopes");
                 double pauseNumber = 0.0D;
                 if (scopeRecord != null) {
                   DataPoint[] scopePoints = scopeRecord.getDataPoints();
                   for (int i = 0; i < scopePoints.length; i++) {
                     if (scopePoints[i].getRawY() == VGCGCScopes.GLOBAL_INT) {
                       pauseNumber += 1.0D;
                     }
                   }
                 }
                 return pauseNumber;
               }
             }


